﻿using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;

namespace iTool.Utils
{
    public static partial class iTypeExtension
    {
        /// <summary>
        /// 根据接口获取类型
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public static Type[] GetTypesByInterface<T>()
            where T : class
        {
            return AppDomain.CurrentDomain.GetAssemblies()
                .SelectMany(a => a.GetTypes().Where(t => t.GetInterfaces().Contains(typeof(T))))
                .ToArray();
        }

        public static TOut CreateInstanceByType<TOut>(Type type, Func<Type, object> getService)
        {
            object[] parametes = new object[0];
            var constractorParameter = type.GetConstructors().Single().GetParameters();
            if (constractorParameter.Count() > 0)
            {
                var constractorParameterTypes = constractorParameter.Select(a => a.ParameterType).ToArray();
                parametes = new object[constractorParameterTypes.Length];
                for (int i = 0; i < constractorParameterTypes.Count(); i++)
                {
                    parametes[i] = getService.Invoke(constractorParameterTypes[i]);
                }
            }
            return (TOut)Activator.CreateInstance(type, parametes);
        }

        public static object CreateInstanceByType(Type type, Func<Type, object> getService)
        {
            object[] parametes = new object[0];
            var constractorParameter = type.GetConstructors().Single().GetParameters();
            if (constractorParameter.Count() > 0)
            {
                var constractorParameterTypes = constractorParameter.Select(a => a.ParameterType).ToArray();
                parametes = new object[constractorParameterTypes.Length];
                for (int i = 0; i < constractorParameterTypes.Count(); i++)
                {
                    parametes[i] = getService.Invoke(constractorParameterTypes[i]);
                }
            }
            return Activator.CreateInstance(type, parametes);
        }

        public static async Task<dynamic> InvokeMethod(object instance, MethodInfo method, Dictionary<string, string> parameters, Func<Type, object> getParamsterFunc = null)
        {
            var methodParameters = method.GetParameters();
            object[] parames = new object[methodParameters.Length];
            bool isTaskVoid = method.ReturnType == typeof(Task);
            bool isTask = (method.ReturnType.Namespace == "System.Threading.Tasks" && method.ReturnType.Name.StartsWith("Task`"));


            if (methodParameters.Length > 0)
            {
                for (int i = 0; i < methodParameters.Length; i++)
                {
                    var parameter = methodParameters[i];
                    if (parameters.ContainsKey(parameter.Name))
                    {
                        if (parameter.ParameterType == typeof(string))
                        {
                            parames[i] = parameters[parameter.Name];
                        }
                        else
                        {
                            parames[i] = JsonConvert.DeserializeObject(parameters[parameter.Name], parameter.ParameterType);
                        }
                    }
                    else
                    {
                        if (getParamsterFunc != null && !parameter.ParameterType.IsValueType)
                        {
                            parames[i] = getParamsterFunc.Invoke(parameter.ParameterType);
                        }
                        else
                        {
                            parames[i] = parameter.ParameterType.IsValueType ? Activator.CreateInstance(parameter.ParameterType) : null;
                        }
                    }
                }
            }

            if (isTask || isTaskVoid)
            {
                if (isTaskVoid)
                {
                    await (dynamic)method.Invoke(instance, parames);
                    return null;
                }
                else
                {
                    return await (dynamic)method.Invoke(instance, parames);
                }
            }
            else
            {
                return method.Invoke(instance, parames);
            }
        }

        public static Type[] GetTypesByAttribute<T>(Assembly[] assemblies = null)
            where T : Attribute
        {
            return (assemblies ?? AppDomain.CurrentDomain.GetAssemblies())
                .SelectMany(a => a.GetTypes().Where(t => t.GetCustomAttribute(typeof(T)) != null))
                .ToArray();
        }
    }
}
